package com.yichen.codesandbox.service.impl;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.dfa.FoundWord;
import cn.hutool.dfa.WordTree;
import com.yichen.codesandbox.model.dto.ExecutionCodeRequest;
import com.yichen.codesandbox.model.dto.ExecutionCodeResponse;
import com.yichen.codesandbox.model.dto.ExecutionMessage;
import com.yichen.codesandbox.model.dto.JudgeInfo;
import com.yichen.codesandbox.service.CodeSandBox;
import com.yichen.codesandbox.utils.ProcessUtils;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

/**
 * @Author: yichenwu
 * @Date:2024/6/25 10:06
 */

/**
 * Java 原生代码沙箱
 */
public class JavaNativeCodeSandBox implements CodeSandBox {
    private final static String GLOBAL_CODE_DIR_NAME = "tempCode";
    private final static String GLOBAL_JAVA_CLASS_NAME = "Main.java";
    private final static WordTree WORD_TREE = new WordTree();
    private final static List<String> BLACKLIST = Arrays.asList("Files", "exec","write");

    static {
        WORD_TREE.addWords(BLACKLIST);
    }

    public static void main(String[] args) {
        // 测试编译
        JavaNativeCodeSandBox javaNativeCodeSandBox = new JavaNativeCodeSandBox();
        ExecutionCodeRequest executionCodeRequest = new ExecutionCodeRequest();
        String code = ResourceUtil.readStr("code/RunDangerousProgram.java", StandardCharsets.UTF_8);
        executionCodeRequest.setCode(code);
        executionCodeRequest.setInput(Arrays.asList("1 2", "3 4"));
        javaNativeCodeSandBox.executeCode(executionCodeRequest);
    }

    @Override
    public List<ExecutionCodeResponse> executeCode(ExecutionCodeRequest executionCodeRequest) {
        try {
            // 1. 把用户的代码保存为文件
            String userDir = System.getProperty("user.dir");
            String globalCodePathName = userDir + File.separator + GLOBAL_CODE_DIR_NAME;
            // 如果不存在该全局目录则创建
            if (!FileUtil.exist(globalCodePathName)) {
                FileUtil.mkdir(globalCodePathName);
            }
            // 将用户代码隔离存放
            String userCodeParentPath = globalCodePathName + File.separator + UUID.randomUUID();
            String userCodePath = userCodeParentPath + File.separator + GLOBAL_JAVA_CLASS_NAME;
                // 校验用户代码中是否有违禁词
                String code = executionCodeRequest.getCode();
                FoundWord foundWord = WORD_TREE.matchWord(code);
                if (foundWord != null) {
                    System.out.println("违禁词警告！！！" + foundWord.getFoundWord());
                    return null;
                }
            File userCodefile = FileUtil.writeString(code, userCodePath, StandardCharsets.UTF_8);
            // 2. 编译代码，得到 class 文件
            ExecutionMessage compileMessage = ProcessUtils.processCompile(userCodefile);
            // 3. 执行代码，得到输出结果
//        Long maxExecuteTime = 0L;
            List<ExecutionCodeResponse> executionCodeResponseList = new ArrayList();
            for (String input : executionCodeRequest.getInput()) {
                List<String> outputList = new ArrayList();
                StopWatch stopWatch = new StopWatch();
                stopWatch.start();
                // 一系列保护措施 -》超时控制、内存限制等
                ExecutionMessage runMessage = ProcessUtils.processRun(input, userCodeParentPath);
                // 交互式，只做实现测试，不在程序中使用
    //            ExecutionMessage runMessage = ProcessUtils.processInteractRun(input, userCodeParentPath);
                stopWatch.stop();
                long totalTimeMillis = stopWatch.getTotalTimeMillis();
    //            maxExecuteTime = maxExecuteTime > stopWatch.getTotalTimeMillis() ? maxExecuteTime : stopWatch.getTotalTimeMillis();
                // 4. 收集整理输出结果
                ExecutionCodeResponse executionCodeResponse = new ExecutionCodeResponse();
                executionCodeResponse.setMessage(runMessage.toString());
                executionCodeResponse.setStatus(runMessage.getExitValue());
                JudgeInfo judgeInfo = new JudgeInfo();
                if (runMessage.getExecuteMessage() != null) {
                    outputList.add(runMessage.getExecuteMessage());
                    judgeInfo.setMessage(runMessage.getExecuteMessage());
                } else {
                    outputList.add(runMessage.getExecuteErrorMessage());
                    judgeInfo.setMessage(runMessage.getExecuteErrorMessage());
                }
                // java 原生很难获取内存，这里不予实现
                judgeInfo.setMemory(null);
                judgeInfo.setTime(totalTimeMillis);
                executionCodeResponse.setJudgeInfo(judgeInfo);
                executionCodeResponse.setOutput(outputList);
                executionCodeResponseList.add(executionCodeResponse);
                System.out.println(executionCodeResponse);
            }

            // 5. 文件清理，释放空间
            if (userCodefile.getParentFile() != null) {
                boolean del = FileUtil.del(userCodeParentPath);
                System.out.println(del ? "成功" : "失败");
            }
            return executionCodeResponseList;
        } catch (Exception e) {
            List<ExecutionCodeResponse> errorExecutionCodeResponse = getErrorExecutionCodeResponse(e);
            return errorExecutionCodeResponse;
        }
    }

    // 6. 错误处理，提升程序健壮性
    private List<ExecutionCodeResponse> getErrorExecutionCodeResponse(Throwable e) {
        List<ExecutionCodeResponse> executionCodeResponseList = new ArrayList();
        ExecutionCodeResponse executionCodeResponse = new ExecutionCodeResponse();
        executionCodeResponse.setMessage(e.getMessage());
        executionCodeResponse.setStatus(2);
        executionCodeResponse.setJudgeInfo(new JudgeInfo());
        executionCodeResponse.setOutput(new ArrayList<>());
        executionCodeResponseList.add(executionCodeResponse);
        return executionCodeResponseList;
    }
}
